package com.zjut.SimpleRPC.core.client;

import com.zjut.SimpleRPC.core.dto.RpcRequest;
import com.zjut.SimpleRPC.core.dto.RpcResponse;
import io.netty.channel.Channel;
import org.apache.log4j.Logger;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.UUID;

/**
 * Created by Ryan on 2016/11/25.
 */
public class RpcClientProxy implements RpcProxy {

    public static final Logger logger = Logger.getLogger(RpcClientProxy.class);

    private RpcClient rpcClient;

    private Channel channel;

    private Map<String, RpcResponse> rpcResponses;

    public RpcClientProxy(RpcClient rpcClient, Map<String, RpcResponse> rpcResponses) {
        this.rpcClient = rpcClient;
        this.rpcResponses = rpcResponses;
    }

    public void setChannel(Channel channel) {
        this.channel = channel;
    }

    public void setRpcClient(RpcClient rpcClient) {
        this.rpcClient = rpcClient;
    }

    @SuppressWarnings("unchecked")
    public <T> T create(Class<?> interfaceClass) {
        return (T) Proxy.newProxyInstance(
                interfaceClass.getClassLoader(),
                new Class<?>[]{interfaceClass},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //封装rpcRequest
                        RpcRequest rpcRequest = new RpcRequest();
                        String s = UUID.randomUUID().toString();
                        rpcRequest.setRequestId(s);
                        rpcRequest.setClassName(method.getDeclaringClass().getName());
                        rpcRequest.setMethodName(method.getName());
                        rpcRequest.setParameters(args);
                        rpcRequest.setParameterTypes(method.getParameterTypes());
                        //发送rpcRequest
                        channel.writeAndFlush(rpcRequest);

                        RpcResponse rpcResponse = new RpcResponse();
                        rpcResponse.setRequestId(s);
                        rpcResponses.put(s, rpcResponse);

                        synchronized (rpcResponse) {
                            rpcResponse.wait();
                        }
                        if (rpcResponse.getError() != null) {
                            throw new Throwable(rpcResponse.getError());
                        }
                        return rpcResponse.getResult();
                    }
                }
        );
    }
}
